// ARDIEx1.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "SOCK_WrapperFacade.h"
#include "Thread_Manager.h"

#define DEFAULT_PORT 27015
#define _SELECT_IMPL  1 // Define to tell server to use the select function 

void *client_handler(void *arg) {
	
	SOCKET h = reinterpret_cast <SOCKET> (arg);
	char buffer[500];

	printf("Client handler started\n");
	SOCK_Stream stream(h);

	memset(buffer, 0, sizeof(buffer));
	size_t n = stream.recv_n(buffer, sizeof(buffer), 0);
	if (n > 0) printf(buffer);

	printf("Client handler ended\n");
	return 0;
}

int main(int argc, char* argv[])
{

	unsigned long address = 0x7F000001;
	// Convert server address to hexadecimal base.
	if (argc > 1) {
		printf("Server set as %s\n", argv[1]);
		address = ntohl(inet_addr(argv[1]));
	} else
		printf("Server set as localhost\n");

	// On windows start socket 2.2 system
	WSADATA wsa_data;
	WORD version_requested = MAKEWORD(2, 2);
	int error = WSAStartup(version_requested, &wsa_data);
	if (error != 0) return -1;

	Thread_Manager threadManager;
	INET_Addr addr(DEFAULT_PORT, address);
	SOCK_Acceptor server(addr); 
	SOCK_Stream *new_stream;

#if _SELECT_IMPL

	// Handle to client connection reading data
	SOCKET client_handle = 0;

	// Set of sockets for which a given status is requested
	FD_SET readfds;
	FD_SET writefds;
	FD_SET exceptfds;

	// Initialize fd sets
	FD_ZERO(&readfds); // Connect and read
	FD_ZERO(&writefds); // Write
	FD_ZERO(&exceptfds); // Errors
	FD_SET(server.get_handle(), &readfds);
	FD_SET(server.get_handle(), &exceptfds);

	while (true)
	{

		printf(".");
		if (select(0, &readfds, &writefds, &exceptfds, 0) > 0) // Block wait for connection
		{
			if (FD_ISSET(server.get_handle(), &readfds))
			{
				new_stream = new SOCK_Stream();
				server.accepting(*new_stream);
				printf("Connect from client\n");
				client_handle = new_stream->get_handle();
				FD_SET(client_handle, &writefds);
				FD_SET(client_handle, &exceptfds);
			}
			if (FD_ISSET(client_handle, &writefds))
			{
				char buffer[500];
				memset(buffer, 0, sizeof(buffer));
				
				// Making it a non blocking socket and read
				u_long nNoBlock = 1;
				ioctlsocket(new_stream->get_handle(), FIONBIO, &nNoBlock);
				int n = new_stream->recv_n(buffer, sizeof(buffer), 0);
				if (n < 0) { 
					delete new_stream; // Close socket
					//Sleep(100);
					FD_CLR(client_handle, &writefds); // Remove handlers
					FD_CLR(client_handle, &exceptfds);
					FD_SET(server.get_handle(), &readfds); // Add handlers - why!
					FD_SET(server.get_handle(), &exceptfds);
				}
				else
				{
					//printf("Reading from client\n");
					printf(buffer);
				}
				// Really unsure when to call FD_CLR
				//Sleep(1000);
			}
			if (FD_ISSET(server.get_handle(), &exceptfds))
			{
				printf("Server connect socket error: %ld.\n", WSAGetLastError());
			}
			if (FD_ISSET(client_handle, &exceptfds))
			{
				printf("Server client socket error: %ld.\n", WSAGetLastError());
			}
		}
		else
		{ 
			printf("!");
			printf("Server select error: %ld.\n", WSAGetLastError());
			Sleep(2000);
		}

	}


#else
	new_stream = new SOCK_stream();
	for (;;) {
		printf("Server started waiting for connection\n");
		server.accepting(*new_stream);
		SOCKET h = new_stream->get_handle();

		// Spawn off a thread-per-connection
		threadManager.spawn(client_handler, reinterpret_cast<void *> (h), 0); 
	}
#endif

	WSACleanup();
	return 0;
}

